export function patch(oldVnode, vnode) {
  // console.log(oldVnode, vnode);
  if (!oldVnode) {
    //oldVnode没有值时， 说明是组件的挂载， vm.$mount()
    // 通过当前的虚拟节点 创建元素并返回
    return createElm(vnode);
  } else {
    const isRealElement = oldVnode.nodeType;
    // 判断是新建还是更新，通过oldNode的类型
    if (isRealElement) {
      //说明是html元素，刚开始创建
      const oldElm = oldVnode;
      const parentElm = oldElm.parentNode;
      let el = createElm(vnode);
      parentElm.appendChild(el, oldElm.nextSibling);
      parentElm.removeChild(oldVnode);
      return el;
    }
    // 两个都是虚拟节点，进行比对，更新
    else{
      // console.log(oldVnode, vnode);
      if(oldVnode.tag !== vnode.tag){ //1: 新旧节点标签不相同
        oldVnode.el.parentNode.replaceChild(createElm(vnode), oldVnode.el);
      }else{

      }
      if(!oldVnode.tag){ //2: 标签不存在，说明是文本节点
        if(oldVnode.text !== vnode.text){
          oldVnode.el.textContent = vnode.text;
        }
      }
      // 3: 标签一致，属性不同，而且不是文本
      let el = vnode.el = oldVnode.el;
      updateProperties(vnode, oldVnode.data); //比对属性，并更新属性

      // 4: 对比子元素
      let oldChildren = oldVnode.children || [];
      let newChildren = vnode.children || [];
      // console.log(oldChildren, newChildren);
      if(oldChildren.length > 0 && newChildren.length > 0){ //新旧节点都有子元素节点
        // Vue diff核心对比算法
        updateChildren(el, oldChildren, newChildren);
      }else if(newChildren.length > 0){ // 旧元素无子节点，新的元素有子节点，直接将新的所有子节点，插入el元素
        for(let i = 0; i < newChildren.length; i++){
          el.appendChild(createElm(newChildren[i]));
        }
      }else if(oldChildren.length > 0){ // 新的无子节点，老的元素有子节点，将子节点全部删除
        el.innerHTML = '';
      }

    }
  }
}

function createComponent(vnode) {
  // 创建组件实例
  let i = vnode.data;
  if ((i = i.hook) && (i = i.init)) {
    i(vnode);
  }
  if (vnode.componentInstance) {
    return true;
  }
}

// 根据vnode虚拟dom创建真实的html元素
export function createElm(vnode) {
  let { tag, children, key, data, text } = vnode;
  if (typeof tag === "string") {
    // 标签
    // 标签有可能是组件
    // 实例化组件
    if (createComponent(vnode)) {
      // 返回真实dom
      return vnode.componentInstance.$el;
    }
    vnode.el = document.createElement(tag);
    updateProperties(vnode);
    children.forEach((child) => {
      return vnode.el.appendChild(createElm(child));
    });
  } else {
    // 文本
    vnode.el = document.createTextNode(text);
  }
  return vnode.el;
}

// 设置属性
function updateProperties(vnode, oldProps = {}) {
  let newProps = vnode.data || {};
  // console.log(newProps, oldProps);

  let el = vnode.el;
  
  // 处理style，如果旧的有，新的无，则直接删除旧的属性
  let oldStyle = oldProps.style;
  let newStyle = newProps.style;
  for(let key in oldStyle) {
    if(!newStyle[key]){
      el.style[key] = '';
    }
  }

  // 如果旧的属性中有，新的属性中无，直接删除该属性
  for(let key in oldProps) {
    if(!newProps[key]){
      el.removeAttribute(key);
    }
  }

  for (let key in newProps) {
    // 如果属性是style
    if (key === "style") {
      for (let styleName in newProps.style) {
        el.style[styleName] = newProps.style[styleName];
      }
      // 如果属性是 class
    } else if (key === "class") {
      el.className = newProps.class;
    } else {
      // 其他属性
      el.setAttribute(key, newProps[key]);
    }
  }
}
function isSameVnode(oldVnode, newVnode) {
  return (oldVnode.tag ==newVnode.tag) && (oldVnode.key === newVnode.key);
}
// 对比子节点
function updateChildren(parent, oldChildren, newChildren){
  // console.log(parent, oldChildren, newChildren);
  // 旧的头和尾，指针index和节点vnode
  let oldStartIndex = 0;
  let oldStartVnode = oldChildren[0];
  let oldEndIndex = oldChildren.length - 1;
  let oldEndVnode = oldChildren[oldEndIndex];

  // 新的头和尾，指针index和节点vnode
  let newStartIndex = 0;
  let newStartVnode = newChildren[0];
  let newEndIndex = newChildren.length - 1;
  let newEndVnode = newChildren[newEndIndex];

  const makeIndexByKey = (children) =>{
    let map = {};
    children.forEach((item, index) => {
      if(item.key){
        map[item.key] = index;
      }
    })
    return map;
  }
  let map = makeIndexByKey(oldChildren);
  // console.log(makeIndexByKey(oldChildren));

  // 对比过程中，新旧虚拟节点双指针循环，如果有新的头尾或者旧的头尾相同，则终止循环
  while(oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex){
    //  优化下一个元素为空
    if(!oldStartVnode){ // 移动过程中，下一个节点为undefined
      oldStartVnode = oldChildren[++oldStartIndex];
    }else if(!oldEndVnode){ // 往前移动时，前一个节点为undefined
      oldEndVnode = oldChildren[--oldEndIndex];
    }
    // 尾部添加元素
    if(isSameVnode(oldStartVnode, newStartVnode)){ //@1: 对比子元素的第一种情况，【从开头对比】新旧节点相同，直接对比属性，进入patch逻辑
      patch(oldStartVnode, newStartVnode);
      // 属性比对完成，给新旧节点的指针向后移动
      oldStartVnode = oldChildren[++oldStartIndex];
      newStartVnode = newChildren[++newStartIndex];
    } else if(isSameVnode(oldEndVnode, newEndVnode)){ //@1: 对比子元素的第一种情况，【从尾部对比】，头部添加元素
      patch(oldEndVnode, newEndVnode);
      oldEndVnode = oldChildren[--oldEndIndex];
      newEndVnode = newChildren[--newEndIndex];
    }
    //@2: 对比子元素的第二种情况，【旧头新尾对比】
    else if(isSameVnode(oldStartVnode, newEndVnode)){
      patch(oldStartVnode, newEndVnode);
      parent.insertBefore(oldStartVnode.el, oldEndVnode.el.nextSibling); //把旧的头，插入到旧的尾部
      // 移动指针
      oldStartVnode = oldChildren[++oldStartIndex];
      newEndVnode = newChildren[--newEndIndex];
    } 
    //@2: 对比子元素的第二种情况，【旧尾新头对比】
    else if(isSameVnode(oldEndVnode, newStartVnode)){
      patch(oldEndVnode, newStartVnode);
      parent.insertBefore(oldEndVnode.el, oldStartVnode.el); //直接将尾部插入到头
      // 移动指针
      oldEndVnode = oldChildren[--oldEndIndex];
      newStartVnode = newChildren[++newStartIndex];
    }else{
      // 无规律的 暴力比对
      // 根据key判断元素能否复用
      // 根据可以创建映射表
      let moveIndex = map[newStartVnode.key];
      if(!moveIndex){ // 新的key在旧的里面找不到，不能复用
        parent.insertBefore(createElm(newStartVnode), oldStartVnode.el);
      }else{ //通过key可以在旧的里面找到，进行复用，移动操作
        let moveVnode = oldChildren[moveIndex];
        oldChildren[moveIndex] = undefined; //将移动的元素置空, 防止数组塌陷
        parent.insertBefore(moveVnode.el, oldStartVnode.el);
        patch(moveVnode, newStartVnode);
      }
      newStartVnode = newChildren[++newStartIndex]; // 新元素 向后移动指针
    }
  }
  // 指针一方重合后，跳出循环，如果新的节点多于旧节点。
  if(newStartIndex <= newEndIndex){
    for(let i = newStartIndex; i<= newEndIndex; i++){
      // 将新的子节点，直接插入[新增的元素，有可能向后增加，也有可能向前增加]，使用dom的insertBefore方法
      // parent.appendChild(createElm(newChildren[i]))
      let el = newChildren[newEndIndex+1] == null ? null : newChildren[newEndIndex+1].el;
      parent.insertBefore(createElm(newChildren[i]), el) // insertBefore可以向指定元素之前进行插入
    }
  }
  // 执行对比完毕后，旧的头尾指针么有重合，则需要把旧的元素给移除
  if(oldStartIndex <= oldEndIndex){ 
    for(let i = oldStartIndex; i <=oldEndIndex; i++){
      let child = oldChildren[i];
      if(child != undefined){
        parent.removeChild(child.el);
      }
    }
  }
}
